#include <stdint.h>
#include "stm32f4xx_hal.h"
#include "delay.h"
#include "debug.h"
#include "tg028q_fsmc.h"


/**
 * @brief
 *
 */
typedef enum
{
    Command = 0,
    Parameter,
} ctrlmode_e;
/**
 * @brief LCD SYSTEM FUNCTION COMMAND TABLE
 *
 */
typedef enum
{
    NOP = 0x00,     /*  */
    SWRESET = 0x01, /* Software Reset */
    RDDID = 0x04,   /* Read Display ID */
    SLPOUT = 0x11,  /* Sleep Out */
    DISPOFF = 0x28, /* Display Off */
    DISPON = 0x29,  /* Display On */
    CASET = 0x2A,   /* Column Address Set */
    RASET = 0x2B,   /* Row Address Set */
    RAMWR = 0x2C,   /* Memory Write */
    RAMRD = 0x2E,   /* Memory Read */
    MADCTL = 0x36,  /* Memory Data Access Control */
    VSCSAD = 0x37,  /* Vertical Scroll Start Address of RAM*/
    COLMOD = 0x3A,  /* Interface Pixel Format */
    PORCTRL = 0xB2, /* Porch Setting */
} FUNCTIONCOMMAND_e;
/**
 * @brief
 *
 */
typedef struct
{
    volatile uint16_t cmd;
    volatile uint16_t para;
} LCD_TypeDef;
/**
 * @brief
 *
 */
#ifndef LCD_BASE
#define LCD_BASE ((uint32_t)(0x6C000000 | 0x001FFFFE))
#endif
/**
 * @brief
 *
 */
#ifndef LCD
#define LCD ((LCD_TypeDef *)LCD_BASE)
#endif

/**
 * @brief
 *
 */
static void LCD_HwReset(void)
{
    /* IM0 SET LOW */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, GPIO_PIN_RESET);

    /* wrtie LCD hardware reset LOW */
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);
    delay_ms(20);

    /* write LCD hardware reset HIGH */
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);
    delay_ms(50);

    /* TURN ON  LCD Backlight */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);
}
/**
 * @brief
 *
 * @param m
 * @param data
 */
static void Write(ctrlmode_e m, uint16_t data)
{
    if (m)
        LCD->para = data;
    else
        LCD->cmd = data;
}
/**
 * @brief
 *
 * @return uint6_t
 */
static uint16_t Read(void)
{
    return LCD->para;
}
/**
 * @brief Read ST7789V Display ID
 *
 * @return uint32_t Display ID
 */
uint32_t tg028q_readID(void)
{
    uint32_t id = 0;
    Write(Command, RDDID);
    id = Read();

    id = Read();
    id |= Read() << 8;
    id |= Read() << 16;
    return id;
}
/**
 * @brief
 *
 */
int32_t tg028q_init(void)
{
    uint32_t lcdid;
    LCD_HwReset();

    delay_ms(120); // ms
    lcdid = tg028q_readID();
    PRINTF_DEBUG("lcdid %08x\r\n", lcdid);

    Write(Command, SLPOUT);

    delay_ms(120); // ms
    /* MADCTL (36h): Memory Data Access Control */
    Write(Command, MADCTL);
    /*
    Bit D7- Page Address Order
        “0” = Top to Bottom (When MADCTL D7=”0”).
        “1” = Bottom to Top (When MADCTL D7=”1”).
    Bit D6- Column Address Order
        “0” = Left to Right (When MADCTL D6=”0”).
        “1” = Right to Left (When MADCTL D6=”1”).
    Bit D5- Page/Column Order
        “0” = Normal Mode (When MADCTL D5=”0”).
        “1” = Reverse Mode (When MADCTL D5=”1”)
    Note: Bits D7 to D5, alse refer to section 8.12 Address Control
    Bit D4- Line Address Order
        “0” = LCD Refresh Top to Bottom (When MADCTL D4=”0”)
        “1” = LCD Refresh Bottom to Top (When MADCTL D4=”1”)
    Bit D3- RGB/BGR Order
        “0” = RGB (When MADCTL D3=”0”)
        “1” = BGR (When MADCTL D3=”1”)
    Bit D2- Display Data Latch Data Order
        “0” = LCD Refresh Left to Right (When MADCTL D2=”0”)
        “1” = LCD Refresh Right to Left (When MADCTL D2=”1”)

    */
    Write(Parameter, 0x00);
    /*COLMOD (3Ah): Interface Pixel Format*/
    Write(Command, COLMOD);
    // Write(Parameter, 0x06);
    Write(Parameter, 0x55);
    /*PORCTRL (B2h): Porch Setting*/
    Write(Command, PORCTRL);
    Write(Parameter, 0x0C);
    Write(Parameter, 0x0C);
    Write(Parameter, 0x00);
    Write(Parameter, 0x33);
    Write(Parameter, 0x33);
    /*GCTRL (B7h): Gate Control*/
    Write(Command, 0xB7);
    Write(Parameter, 0x75); // VGH=14.97V, VGL=-10.43V
                            /*VCOMS (BBh): VCOM Setting*/
    Write(Command, 0xBB);   // VCOM
    Write(Parameter, 0x1F);
    /*LCMCTRL (C0h): LCM Control*/
    Write(Command, 0xC0);
    Write(Parameter, 0x2C);
    /*VDVVRHEN (C2h): VDV and VRH Command Enable*/
    Write(Command, 0xC2);
    Write(Parameter, 0x01);
    /*VRHS (C3h): VRH Set*/
    Write(Command, 0xC3); // GVDD
    Write(Parameter, 0x13);
    /*VDVS (C4h): VDV Set*/
    Write(Command, 0xC4);
    Write(Parameter, 0x20);
    /*FRCTRL2 (C6h): Frame Rate Control in Normal Mode*/
    Write(Command, 0xC6);
    Write(Parameter, 0x0F);
    /*PWCTRL1 (D0h): Power Control 1*/
    Write(Command, 0xD0);
    Write(Parameter, 0xA4);
    Write(Parameter, 0xA1);

    Write(Command, 0xD6);
    Write(Parameter, 0xA1);
    /*PVGAMCTRL (E0h): Positive Voltage Gamma Control*/
    Write(Command, 0xE0);
    Write(Parameter, 0xD0);
    Write(Parameter, 0x0D);
    Write(Parameter, 0x16);
    Write(Parameter, 0x14);
    Write(Parameter, 0x14);
    Write(Parameter, 0x2F);
    Write(Parameter, 0x38);
    Write(Parameter, 0x54);
    Write(Parameter, 0x48);
    Write(Parameter, 0x0A);
    Write(Parameter, 0x1C);
    Write(Parameter, 0x19);
    Write(Parameter, 0x1A);
    Write(Parameter, 0x1D);
    /*NVGAMCTRL (E1h): Negative Voltage Gamma Control*/
    Write(Command, 0xE1);
    Write(Parameter, 0xD0);
    Write(Parameter, 0x04);
    Write(Parameter, 0x0C);
    Write(Parameter, 0x08);
    Write(Parameter, 0x09);
    Write(Parameter, 0x23);
    Write(Parameter, 0x39);
    Write(Parameter, 0x54);
    Write(Parameter, 0x48);
    Write(Parameter, 0x30);
    Write(Parameter, 0x14);
    Write(Parameter, 0x14);
    Write(Parameter, 0x19);
    Write(Parameter, 0x1C);
    /* Display On */
    Write(Command, DISPON);
    /* Memory Write */
    Write(Command, RAMWR);
    for (int i = 0; i < TG028Q_V / 3; i++)
    {
        for (int j = 0; j < TG028Q_H; j++)
        {
            Write(Parameter, RED);
        }
    }
    for (int i = TG028Q_V / 3; i < TG028Q_V * 2 / 3; i++)
    {
        for (int j = 0; j < TG028Q_H; j++)
        {
            Write(Parameter, GREEN);
        }
    }
    for (int i = TG028Q_V * 2 / 3; i < TG028Q_V; i++)
    {
        for (int j = 0; j < TG028Q_H; j++)
        {
            Write(Parameter, BLUE);
        }
    }
    return 0;
}
/**
 * @brief LCD 清屏
 *
 * @param color 清屏颜色
 */
void tg028q_clear(uint16_t color)
{
    Write(Command, RAMWR);
    for (int i = 0; i < TG028Q_V; i++)
    {
        for (int j = 0; j < TG028Q_H; j++)
        {
            Write(Parameter, color);
        }
    }
}
void tg028q_set_region(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
	if(TG028Q_DISPLAY_DIR==0)
	{
		Write(Command,CASET);//列地址设置
		Write(Parameter,x1>>8);
		Write(Parameter,x1);
		Write(Parameter,x2>>8);
		Write(Parameter,x2);
		Write(Command,RASET);//行地址设置
		Write(Parameter,y1>>8);
		Write(Parameter,y1);
		Write(Parameter,y2>>8);
		Write(Parameter,y2);
		Write(Command,RAMWR);//储存器写
	}
	else if(TG028Q_DISPLAY_DIR==1)
	{
		Write(Command,CASET);//列地址设置
		Write(Parameter,x1>>8);
		Write(Parameter,x1);
		Write(Parameter,x2>>8);
		Write(Parameter,x2);
		Write(Command,RASET);//行地址设置
		Write(Parameter,y1>>8);
		Write(Parameter,y1);
		Write(Parameter,y2>>8);
		Write(Parameter,y2);
		Write(Command,RAMWR);//储存器写
	}
	else if(TG028Q_DISPLAY_DIR==2)
	{
		Write(Command,CASET);//列地址设置
		Write(Parameter,x1>>8);
		Write(Parameter,x1);
		Write(Parameter,x2>>8);
		Write(Parameter,x2);
		Write(Command,RASET);//行地址设置
		Write(Parameter,y1>>8);
		Write(Parameter,y1);
		Write(Parameter,y2>>8);
		Write(Parameter,y2);
		Write(Command,RAMWR);//储存器写
	}
	else
	{
		Write(Command,CASET);//列地址设置
		Write(Parameter,x1>>8);
		Write(Parameter,x1);
		Write(Parameter,x2>>8);
		Write(Parameter,x2);
		Write(Command,RASET);//行地址设置
		Write(Parameter,y1>>8);
		Write(Parameter,y1);
		Write(Parameter,y2>>8);
		Write(Parameter,y2);
		Write(Command,RAMWR);//储存器写
	}
}
void tg028q_drawpoint(uint16_t x,uint16_t y,uint16_t color)
{
    tg028q_set_region(x,y,x,y);
    Write(Parameter,color);
}
void tg028q_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)
{
	uint16_t i, j;
	uint16_t xlen = 0;
    tg028q_set_region(sx,sy,ex,ey);
    xlen = ex - sx + 1;
    for (i = sy; i <= ey; i++)
    {
        for (j = 0; j < xlen; j++)
            Write(Parameter,color);
    }
}
